import 'package:bonsoir/bonsoir.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/services/device_info/device_info.dart';

class ConnectClientsState {
  final List<BonsoirService> services;
  final ResolvedBonsoirService? resolvedService;
  final BonsoirDiscovery discovery;

  ConnectClientsState({
    required this.services,
    required this.discovery,
    this.resolvedService,
  });

  ConnectClientsState copyWith({
    List<BonsoirService>? services,
    BonsoirDiscovery? discovery,
    ResolvedBonsoirService? resolvedService,
  }) {
    return ConnectClientsState(
      services: services ?? this.services,
      discovery: discovery ?? this.discovery,
      resolvedService: resolvedService ?? this.resolvedService,
    );
  }
}

class ConnectClientsNotifier extends AsyncNotifier<ConnectClientsState> {
  ConnectClientsNotifier();

  @override
  build() async {
    final discovery = BonsoirDiscovery(type: '_spotube._tcp');
    final deviceId = await DeviceInfoService.instance.deviceId();
    await discovery.ready;

    final subscription = discovery.eventStream?.listen((event) {
      // ignore device itself
      if (event.service?.attributes["deviceId"] == deviceId) {
        return;
      }

      switch (event.type) {
        case BonsoirDiscoveryEventType.discoveryServiceFound:
          state = AsyncData(state.value!.copyWith(
            services: [
              ...?state.value?.services,
              event.service!,
            ],
          ));
          break;
        case BonsoirDiscoveryEventType.discoveryServiceResolved:
          state = AsyncData(
            state.value!.copyWith(
              resolvedService: event.service as ResolvedBonsoirService,
            ),
          );
          break;
        case BonsoirDiscoveryEventType.discoveryServiceLost:
          state = AsyncData(
            ConnectClientsState(
              services: state.value!.services
                  .where((s) => s.name != event.service!.name)
                  .toList(),
              discovery: state.value!.discovery,
              resolvedService: state.value?.resolvedService != null &&
                      event.service?.name == state.value?.resolvedService?.name
                  ? null
                  : state.value!.resolvedService,
            ),
          );
          break;
        default:
          break;
      }
    });

    ref.onDispose(() {
      subscription?.cancel();
      discovery.stop();
    });

    await discovery.start();

    return ConnectClientsState(
      services: [],
      discovery: discovery,
    );
  }

  Future<void> resolveService(BonsoirService service) async {
    if (state.value == null) return;
    await service.resolve(state.value!.discovery.serviceResolver);
  }

  Future<void> clearResolvedService() async {
    if (state.value == null) return;
    state = AsyncData(
      ConnectClientsState(
        services: state.value!.services,
        discovery: state.value!.discovery,
      ),
    );
  }
}

final connectClientsProvider =
    AsyncNotifierProvider<ConnectClientsNotifier, ConnectClientsState>(
  () => ConnectClientsNotifier(),
);
